package cn.example.vip.mall.search.service.impl;

import cn.example.vip.mall.search.entity.SkuEs;
import cn.example.vip.mall.search.mapper.SkuSearchMapper;
import cn.example.vip.mall.search.service.SkuSearchService;
import cn.example.vip.mall.util.PageInfo;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SkuSearchServiceImpl implements SkuSearchService {
	@Autowired
	private SkuSearchMapper skuSearchMapper;
	@Autowired
	private ElasticsearchRestTemplate elasticsearchRestTemplate;
	
	/**
	 * 添加 ElasticSearch 索引
	 * @param skuEs SkuEs对象
	 */
	@Override
	public void add(SkuEs skuEs) {
		// 获取搜索索引
		String skuAttributeMap = skuEs.getSkuAttribute();
		
		if(!StringUtils.isEmpty(skuAttributeMap)) {
			Map<String, String> attrMap = JSON.parseObject(skuAttributeMap, Map.class);
			skuEs.setAttrMap(attrMap);
			System.out.println("--------------" + attrMap);
		}
		skuSearchMapper.save(skuEs);
	}
	
	/**
	 * 删除 ElasticSearch 索引
	 * @param id skuId
	 */
	@Override
	public void del(String id) {
		skuSearchMapper.deleteById(id);
	}
	
	/**
	 * 搜索接口调用
	 * @param searchMap 查询参数 Map
	 * @return 返回查询结果
	 */
	@Override
	public Map<String, Object> search(Map<String, Object> searchMap) {
		// QueryBuilder 构建ElasticSearch查询条件
		NativeSearchQueryBuilder queryBuilder = queryBuilder(searchMap);
		
		// 调用以"品牌与分类"为条件的分组搜索参数检查，不存在参数则查询所有 (传入对象引用，内部修改有效)
		group(queryBuilder, searchMap);
		
		// 搜索结果高亮显示的配置
		HighlightBuilder.Field field = new HighlightBuilder.
			Field("name").                      //指定的高亮域
			preTags("<span style=\"color:red\">").   //前缀
			postTags("</span>").                      //后缀
			fragmentSize(100);
		// 添加高亮域
		queryBuilder.withHighlightFields(field);
		
		// SkuSearchMapper 调用搜索接口
//		Page<SkuEs> skuEsPage = skuSearchMapper.search(queryBuilder.build());	// 分页查询，没有分组功能
//		AggregatedPage<SkuEs> skuEsPage = (AggregatedPage<SkuEs>) skuSearchMapper.search(queryBuilder.build());
		AggregatedPage<SkuEs> skuEsPage = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), SkuEs.class);
		System.out.println("--------" + skuEsPage);
		
		// 获取结果集
		Map<String, Object> resultMap = new HashMap<>();
		
		// 解析分组结果
		Aggregations skuEsGroupPages = skuEsPage.getAggregations();
		parseGroup(skuEsGroupPages, resultMap);	// 解析分组
		parseSkuAttribute(resultMap);	// 解析动态属性
		
		// 分页信息处理
		long total = skuEsPage.getTotalElements();
		int currentPage = queryBuilder.build().getPageable().getPageNumber() + 1;
		int pageSize = queryBuilder.build().getPageable().getPageSize();
		PageInfo pageInfo = new PageInfo(total, currentPage, pageSize);
		
		// 完善结果集
		List<SkuEs> list = skuEsPage.getContent();
		resultMap.put("list", list);
		resultMap.put("total", skuEsPage.getTotalElements());
		resultMap.put("pageInfo", pageInfo);
		
		return resultMap;
	}
	
	/**
	 * 搜索条件构建，获取搜索条件参数：keywords
	 *
	 * 查询规范:
	 * 分类参数:"category"
	 * 品牌参数:"brand"
	 * 价格参数:"price"
	 * 属性参数:"attr_属性名:属性值"
	 * 分页参数:"page"
	 * 排序参数:
	 * 		1."排序域:sfield":price(按价格排序) updateTime(按商品上架时间),
	 * 		2."排序方式:sm":ASC DESC(升序与降序)
	 *
	 * @param searchMap 请求携带的搜索参数，包含关键词
	 * @return 返回ElasticSearch查询结果
	 */
	public NativeSearchQueryBuilder queryBuilder(Map<String, Object> searchMap) {
		// 单一查询
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();	// 新建实例对象
		// 多条件查询
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();	// 工厂类代理创建
		
		if(searchMap != null) {
			// 获取查询的关键词
			Object keyword = searchMap.get("keywords");
			if (!StringUtils.isEmpty(keyword)) {
//				queryBuilder.withQuery(QueryBuilders.termQuery("name", keyword.toString()));
				boolQueryBuilder.must(QueryBuilders.matchQuery("name", keyword.toString()));
			}
			// 获取查询的分类名
			Object categoryName = searchMap.get("category");
			if (!StringUtils.isEmpty(categoryName)) {
				boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName", categoryName.toString()));
			}
			// 获取查询的品牌名
			Object brandName = searchMap.get("brand");
			if (!StringUtils.isEmpty(brandName)) {
				boolQueryBuilder.must(QueryBuilders.matchQuery("brandName", brandName.toString()));
			}
			// 获取查询的价格区间: x-y 0-100 100-1000元 1000元以上
			Object price = searchMap.get("price");
			if (!StringUtils.isEmpty(price)) {
				String[] priceRange = price.toString()    // 格式化处理字符串，获取价格区间
					.replace("元", "")
					.replace("以上", "")
					.split("-");
				// 范围查询
				boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(priceRange[0])));    // price > x
				if (priceRange.length == 2) {
					boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(priceRange[1])));    // price <= y
				}
			}
			// 查询动态属性
			for (Map.Entry<String, Object> entry : searchMap.entrySet()) {
				String searchMapKey = entry.getKey();
				// 查询键名是否以 "attr_" 开头
				if (entry.getKey().startsWith("attr_")) {
					String queryKey = "attrMap." + searchMapKey.replaceFirst("attr_", "") + ".keyword";    // 此处不加keyword
//					boolQueryBuilder.must(QueryBuilders.termQuery(queryKey, entry.getValue()));
					boolQueryBuilder.must(QueryBuilders.termQuery(queryKey, entry.getValue().toString()));
					System.out.println(queryKey + ":" + entry.getValue());
				}
			}
			// 排序
			Object sfield = searchMap.get("sfield");
			Object sm = searchMap.get("sm");
			if(!StringUtils.isEmpty(sfield) && !StringUtils.isEmpty(sm)) {
				queryBuilder.withSort(SortBuilders
					.fieldSort(sfield.toString())				// 排序目标
					.order(SortOrder.valueOf(sm.toString()))	// 排序方式
				);
			}
			
		}
		
		// 分页查询(页数从 0 开始)
		queryBuilder.withPageable(PageRequest.of(currentPage(searchMap), 5));
		// 分页参数设置
		
		return queryBuilder.withQuery(boolQueryBuilder);	// 修复bug： withQuery 附带其他查询工具
	}
	
	public int currentPage(Map<String, Object> searchMap){
		try{
			Object page = searchMap.get("page");
			return Integer.parseInt(page.toString()) - 1;	// 页数到索引值: n -> n-1
		}catch (Exception e){
//			e.printStackTrace();
			return 0;
		}
	}
	
	/**
	 * 分组查询条件检查
	 */
	public void group(NativeSearchQueryBuilder queryBuilder, Map<String,Object> searchMap){
		// 如果前端没有提供分类名参数时，查询所有搜索分类并展示
		if(searchMap.get("category") == null) {
			queryBuilder.addAggregation(AggregationBuilders
				.terms("categoryList")    // 查询数据别名
				.field("categoryName")	// 查询CategoryName分组(添加.keyword)
				.size(100));                	// 查询数目
		}
		// 如果前端没有提供品牌名参数时，查询所有搜索品牌并展示
		if(searchMap.get("brand") == null) {
			queryBuilder.addAggregation(
				AggregationBuilders
					.terms("brandList")	// 别名,
					.field("brandName")	// brand分组(添加.keyword)
					.size(100));				// 查询数目
		}
		// 属性分页查询
		queryBuilder.addAggregation(
			AggregationBuilders
				.terms("attrmaps")
				.field("skuAttribute")	// (添加.keyword)
				.size(100000)
		);
	}
	
	/**
	 * 将属性信息合并成 Map 对象
	 */
	public void parseGroup(Aggregations aggregations, Map<String, Object> resultMap) {
		// 存储结果集
//		Map<String, Object> resultMap = new HashMap<>();
		
		for(Aggregation aggregation : aggregations){
			// 使用 Aggregation 的实现类: ParsedStringTerms 对数据类进行操作
			ParsedStringTerms buckets = (ParsedStringTerms) aggregation;
			// 分组值列表
			List<String> values = buckets.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
			// 分组名
			resultMap.put(aggregation.getName(), values);
		}
	}
	
	public void parseSkuAttribute(Map<String, Object> searchMap) {
		Object attrmaps = searchMap.get("attrmaps");
		if(attrmaps != null) {
			// 集合数据
			List<String> attrList = (List<String>)attrmaps;
			// 定义集合，简化存储 SkuAttribute 结果集
			Map<String, Set<String>> allMaps = new HashMap<>();
			for(String attr : attrList) {
				// 获取键值对: SkuAttribute
				Map<String, String> attrMap = JSON.parseObject(attr, Map.class);
				// 利用forEach特性获取： "学习费用" "就业薪资" 两个键名的值，存储到 Map 对象中，便于处理
				for(Map.Entry<String, String> entry: attrMap.entrySet()){
					// 获取每条记录，填入 allMaps.Set<String>
					String key = entry.getKey();
					// 查询值
					Set<String> values = allMaps.get(key);
					if(values == null) {
						values = new HashSet<>();
					}
					values.add(entry.getValue());
					allMaps.put(key, values);
				}
			}
			//覆盖之前的attrmaps
			searchMap.put("attrmaps",allMaps);
//			System.out.println("allmaps" + allMaps);
		}
	}
	
}
